package require wt::server::dataSources::wtSqlServer

namespace eval ::wt::session::dbSession::odbcSession::sqlServerSession:: {
  variable db ""
  variable sessionTable WtSession
  variable entryTable WtSessionEntry
}

# Load hook

proc ::wt::session::dbSession::odbcSession::sqlServerSession::load {} {
}

# Unload hook

proc ::wt::session::dbSession::odbcSession::sqlServerSession::unload {} {
}

# Open hook

proc ::wt::session::dbSession::odbcSession::sqlServerSession::open {id} {

}

# Close hook

proc ::wt::session::dbSession::odbcSession::sqlServerSession::close {id} {
}

# Open the session database

proc ::wt::session::dbSession::odbcSession::sqlServerSession::openDb {} {
  variable db
  if {$db == ""} {
    set db [::wt::database::dbManager::openDataSource wtDb]
  }
  return $db
}

# Create a new session

proc ::wt::session::dbSession::odbcSession::sqlServerSession::create {id} {
  variable db
  variable sessionTable

  openDb

  # Check if the session exists

  set found 0
  set types {{TIMESTAMP} {CHAR 32}}
  set vals [list [nowSqlString] $id]

  set sessions [$db "select datediff(second, accessTime, ?)
    from $sessionTable where sessionId = ?" $types $vals]

  if {[llength $sessions]} {
    set idleTime [lindex [lindex $sessions 0] 0]
    set maxIdleTime [::wt::session::session maxIdleTime]
    if {$maxIdleTime > 0 && $idleTime > $maxIdleTime} {
      $db "delete from $sessionTable where sessionId = ?" \
        {{CHAR 32}} [list $id]
    } else {
      set found 1
    }
  }

  # Insert the session record

  if {!$found} {
    set types {{CHAR 32} {TIMESTAMP} {TIMESTAMP} {CHAR 15} {CHAR 128}}
    set now [nowSqlString]
    set vals [list $id $now $now [::wt::server::serverVars get REMOTE_ADDR] \
      [::wt::server::serverVars get HTTP_USER_AGENT]]

    $db "insert into $sessionTable (sessionId, creationTime, \
      accessTime, clientIp, clientUserAgent) \
      values (?, ?, ?, ?, ?)" $types $vals
  }

  return [expr !$found]
}

# Fetch all the session entries

proc ::wt::session::dbSession::odbcSession::sqlServerSession::fetch {id} {
  variable db
  variable sessionTable
  variable entryTable

  openDb

  set maxIdleTime [::wt::session::session maxIdleTime]

  if {$maxIdleTime > 0} {
    set types {{CHAR 32} {TIMESTAMP} {INTEGER}}
    set now [nowSqlString]
    set vals [list $id $now $maxIdleTime]
    set sql "select name, value from $entryTable, \
      $sessionTable where \
      $entryTable.sessionId = $sessionTable.sessionId and \
      $entryTable.sessionId = ? and \
      datediff(second, accessTime, ?) <= ?"
    set entries [$db $sql $types $vals]
  } else {
    set types {{CHAR 32}}
    set vals [list $id]
    set sql "select name, value from $entryTable where \
      sessionId = ?"
    set entries [$db $sql $types $vals]
  }

  set items [table create]
  foreach entry $entries {
    table set items [lindex $entry 0] [lindex $entry 1]
  }

  return $items
}

# Write the changes

proc ::wt::session::dbSession::odbcSession::sqlServerSession::update {id updates} {
  variable db
  variable sessionTable
  variable entryTable

  openDb

  set isNew [create $id]

  table importVars $updates

puts "UPDATE: $updates"

  # Handle the deletions

  if {$clearAll} {
    set types {{CHAR 32}}
    set vals [list $id]
    $db "delete from $entryTable where sessionId = ?" $types $vals
  } else {
    set terms [list]
    set types {{CHAR 32}}
    set vals [list $id]

    foreach {key val} $keyDeletions {
      lappend terms {name = ?}
      lappend types {VARCHAR 8000}
      lappend vals $key
    }

    foreach {key val} $deletions {
      lappend terms {(name = ? and value = ?)}
      lappend types {VARCHAR 8000}
      lappend types {VARBINARY 8000}
      lappend vals $key
      lappend vals $val
    }

    if {[llength $terms]} {
      set sql "delete from $entryTable where sessionId = ? and ("
      append sql [join $terms " or "]
      append sql ")"

      puts "SQL: $sql<br>"
      puts "Values: $vals<br>"

      $db $sql $types $vals
    }
  }

  # Handle the insertions

  if {[llength $insertions]} {
    set time [nowSqlString]
    set types [list]
    set vals [list]
    set valsSql ""

    foreach {key val} $insertions {
      lappend types {TIMESTAMP}
      lappend types {VARCHAR 8000}
      lappend types {VARBINARY 8000}
      lappend types {CHAR 32}

      lappend vals $time
      lappend vals $key
      lappend vals $val
      lappend vals $id

      if {$valsSql != ""} {
        append valsSql " UNION ALL "
      }
      append valsSql "SELECT ?, ?, ?, ?"
    }
    set sql "insert into $entryTable (creationTime, name, value, sessionId) $valsSql"

    puts "SQL: $sql<br>"
    puts "Values: $vals<br>"

    $db $sql $types $vals
  }

  # Update the access time

  if {$isAccessed && $updateAccessTime && !$isNew} {
    set types {{TIMESTAMP} {CHAR 32}}
    set vals [list [nowSqlString] $id]
    $db "update $sessionTable set accessTime = ? where sessionId = ?" \
      $types $vals
  }
}

# Properties

proc ::wt::session::dbSession::odbcSession::sqlServerSession::properties {id} {
  variable db
  variable sessionTable

  openDb

  set types {{TIMESTAMP} {CHAR 32}}
  set vals [list [nowSqlString] $id]
  set sessions [$db "select creationTime, accessTime,
    datediff(second, accessTime, ?), clientIp, \
    clientUserAgent from $sessionTable where \
    sessionId = ?" $types $vals]

  set attrs [table create]
  table set attrs status new

  if {[llength $sessions]} {
    set session [lindex $sessions 0]
    table set attrs creationTime [lindex $session 0]
    table set attrs accessTime [lindex $session 1]
    set idleTime [lindex $session 2]
    table set attrs idleTime $idleTime
    table set attrs clientIp [lindex $session 3]
    table set attrs clientUserAgent [lindex $session 4]

    if {$idleTime > [::wt::session::session maxIdleTime]} {
      table set attrs status expired
    } else {
      table set attrs status active
    }
  }

  return $attrs
}

# Status of a session

proc ::wt::session::dbSession::odbcSession::sqlServerSession::status {id} {
  variable db

  openDb

  set status new

  set types {{TIMESTAMP} {CHAR 32}}
  set vals [list [nowSqlString] $id]
  set sessions [$db "select datediff(second, accessTime, ?) \
    from $sessionTable where sessionId = ?" $types $vals]
  if {[llength $sessions]} {
    set idleTime [lindex [lindex $sessions 0] 0]
    if {$idleTime > [::wt::session::session maxIdleTime]} {
      set status expired
    } else {
      set status active
    }
  }

  return $status
}

# Delete a session

proc ::wt::session::dbSession::odbcSession::sqlServerSession::delete {id} {
  variable db

  openDb

  set types {{CHAR 32}}
  set vals [list $id]
  $db "delete from $sessionTable where sessionId = ?" $types $vals
}

# Cleanup stale sessions

proc ::wt::session::dbSession::odbcSession::sqlServerSession::sweep {maxIdleTime} {
  variable db
  openDb

  set n 0

  if {$maxIdleTime > 0} {
    set types {{TIMESTAMP} {INTEGER}}
    set vals [list [nowSqlString] $maxIdleTime]

    variable sessionTable
    set n [$db "delete from $sessionTable where \
      datediff(second, accessTime, ?) > ?" \
      $types $vals]
  }

  return $n
}

proc ::wt::session::dbSession::odbcSession::sqlServerSession::nowSqlString {} {
  set now [clock format [clock seconds] -format "%Y-%m-%d %H:%M:%S"]
}

package provide wt::session::dbSession::odbcSession::sqlServerSession 1
